// JavaScript Document
var http = require('http');
var fs = require('fs');
var path = require('path');
var url = require('url');

var constants = require('./constants.js');
var dao = require('./talk.dao.js');
var timepoint = require('./timepoint.js');
var util = require('./util.js');
var oauth = require('./oauth.js');
var user = require('./user/user.js');
var notification = require('./notification/notification.js');

/*
 * Constants
 */
var logger = util.logger;

var filename = 'fog1.2.6.html';
//var filename = './resources/test.search.html';
var setTimepoint = false;
var postTwitter = false;

var locations = {};		// store client's screen location as defining key as client's socket id and value as location coordination
var tokens = {};		// store oauth request token and secret temporarily

/*
 * Initialization
 */
oauth.initialize(constants.twitter.REQUEST_TOKEN_URL, constants.twitter.ACCESS_TOKEN_URL);

var respond = function(filepath, response) {
	var extname = path.extname(filepath);
    
	// set content type
    var contentType = 'text/html';
    switch (extname) {
        case '.js':
            contentType = 'text/javascript';
            break;
        case '.css':
            contentType = 'text/css';
            break;
    }
    
    // set response
	fs.exists(filepath, function(exists) {
    	if (exists) {
            fs.readFile(filepath, function(error, content) {
                if (error) {
                    response.writeHead(500);
                    response.end();
                }
                else {
                    response.writeHead(200, { 'Content-Type': contentType });
                    response.end(content, 'utf-8');
                }
            });
        }
        else {
            response.writeHead(404);
            response.end();
        }
    });
};

var webserver  = http.createServer(function (request, response) {
	var filepath = '.' + request.url;
    if (filepath == './') filepath = './' + filename;
        
    // deal with OAuth callback
    if (filepath.indexOf('./oauth') > -1) {
    	var params = url.parse(request.url, true).query;
    	var oauth_token = params.oauth_token;
    	console.log('oauth callback url. token: ' + oauth_token + ', ' + params.oauth_verifier);
    	
    	// get access token
    	if (tokens[oauth_token] && tokens[oauth_token].request) {
    		oauth.getAccessToken(oauth_token, tokens[oauth_token].request.secret, function(oauth_access_token, oauth_access_token_secret) {
        		tokens[oauth_token].access = {token: oauth_access_token, secret: oauth_access_token_secret};
        		console.log(JSON.stringify(tokens[oauth_token].access));
        		
        		// if status to be updated exists, post it
        		if (tokens[oauth_token].status) {
        			oauth.post(constants.twitter.POST_URL, {status: tokens[oauth_token].status}, 'text/plain', 
        	    			oauth_access_token, oauth_access_token_secret);
        			delete tokens[oauth_token].status;
        		}
        	});
    	}
    	else {
    		// TODO: handle exception
    	}
    	
    	filepath = './oauth.html';
    }
    
    respond(filepath, response);    
});

// create socket server
var io = require('socket.io').listen(webserver);
io.set('log level', 1);

var broadcast = function(event, cell) {
	var clients = io.sockets.clients();
	var count = 0;
	
	var location = util.getLocation(cell._id);
	logger.debug('broadcast event ' + event + ' to clients.');
	
	clients.forEach(function(client) {
		if (util.isNear(location, locations[client.id])) {
			logger.debug('emit event ' + event + ' to '+ client.id);
			client.emit(event, cell);
			count++;
		}
	});
	
	logger.info('emit event ' + event + ' to ' + count + ' clients.');
};

var onCellPosted = function(cell) {
	broadcast('regist', cell);
	
	// update adjacent cells
	var nears = cell.getNearCellIds();
	for (var i = 0; i < nears.length; i++) {
		dao.get(nears[i], function(near) {
			var wasEmpty = near.isEmpty();
			
			near.addNeighbor(cell._id, function() {
				if (wasEmpty) {
					broadcast('regist', near);
				}
			});
			
			// if this post is related to the near cell, push notification to according users ..Added by Sejoon at 2012.11.18
			console.log('see if master cell.');
			if (near.class == constants.class.MASTER && near.isRelated(cell)) {
				console.log('push notification.');
				/*
				 * push notification
				 * NOTE: cell is not a general doc so need to creaet new doc. 
				 */				
				notification.push('post', {
					targets: near.username, 
					cell: {
						username: cell.username,
						content: cell.content
					}
				});
			}
		});
	}
};

var search = function(condition, handler) {
	dao.find(condition, null, {skip: 0, limit: 5}, function(cells) {
		if(handler) handler(cells);
	});
};

io.sockets.on('connection', function (socket) {
	socket.on('login', function(credential) {
		console.log('check user credential... ' + JSON.stringify(credential));
		user.login(credential, function(result) {
			// return login result as boolean type
			socket.emit('login', result);
			
			if (result) {
				// create user room to make it easier to find user's socket
				socket.join(credential.username);
				socket.set('name', credential.username);
			}			
		});
	});
	
	// ajaj event which occurs document is ready on client browser
	socket.on('ajaj', function (data) {
		logger.info('ajaj event occured.');
		dao.find({
				class: {$in: [constants.class.MASTER, constants.class.SLAVE, constants.class.RESERVED]},
				gridx: {$gte: data.cfx - 64, $lt: data.cfx + 64},
				gridy: {$gte: data.cfy - 24, $lt: data.cfy + 24}}
		, null, null, function(cells) {
			socket.emit('ajaj', cells);
			locations[socket.id] = {x: data.cfx, y: data.cfy};
			
			logger.debug('locations: ' + JSON.stringify(locations));
			logger.debug('location: ' + locations[socket.id].x + ', ' + locations[socket.id].y);
		});
	});
	
	socket.on('reserve', function (data) {
		dao.get(data.id, function(cell) {
			cell.reserve(function() {
				broadcast('reserve', cell);
			});
		});
	});
	
	socket.on('release', function (data) {
		if (data.id) {
			dao.get(data.id, function(cell) {
				cell.release(function() {
					broadcast('release', cell);
				});
			});
		};
	});
	
	// handler for registering or updating a post
	socket.on('regist', function (data) {
		logger.info('data has been registered: ' + data.id);
		
		socket.get('name', function(error, username) {
			dao.get(data.id, function(cell) {
				if (cell.isMaster()) {
					logger.info(data.id + ': cell already populated. push it to adjacent cell.');
					
					// if cell was already popluated, push this post to other near cell randomly
					dao.find({
						_id: {$in: cell.getNearCellIds()}, 
						class: constants.class.SLAVE}
					, null, null, function(cells) {
						if (cells.length == 0) return;
						
						var index = Math.floor(Math.random() * cells.length);
						cells[index].post(username, data.keyword, data.content, data.color, cell._id, function() {
							onCellPosted(cells[index]);
						});
					});
				}
				else {
					cell.post(username, data.keyword, data.content, data.color, null, function() {
						onCellPosted(cell);
					});
				}
			});
		});		
		
		// post to twitter
		if (postTwitter) {
			socket.get('token', function(error, token) {
				if (token && tokens[token] && tokens[token].access) {
					// if access token exists
					oauth.post(constants.twitter.POST_URL, {status: data.content}, 'text/plain', 
	    	    			tokens[token].access.token, tokens[token].access.secret);
				}
				else {
					// if access token does not exist, get request token
					oauth.getRequestToken(function(oauth_token, oauth_token_secret) {
						socket.set('token', oauth_token);
						tokens[oauth_token] = {
							id: socket.id, 
							request: {secret: oauth_token_secret},
							status: data.content
						};
						
						socket.emit('twitter.auth.request', oauth_token);
												
						// DEBUG
						socket.get('token', function(error, token) {
							console.log('token: ' + token);
						});
						
						// TODO: set timeout in order to leave the room and remove tokens object after specific time
					});
				}
			});
		}		
	});
	
	// search for keyword, content and nickname
	socket.on('search', function(keyword) {
		var result = {};
		
		// search for keyword field
		search({class: 'filled', keyword: new RegExp(keyword)}, function(cells) {
			result.keyword = cells;
			
			search({class: 'filled', content: new RegExp(keyword)}, function(cells) {
				result.content = cells;
				
				search({class: 'filled', nick: new RegExp(keyword)}, function(cells) {
					result.nick = cells;
					
//					console.log(JSON.stringify(result.keyword));
//					console.log(JSON.stringify(result.content));
//					console.log(JSON.stringify(result.nick));
					
					socket.emit('search', result);
				});
			});
		});
	});
	
	socket.on('disconnect', function() {
		socket.get('token', function(error, token) {
			delete tokens[token];
		});
		
		delete locations[socket.id];
		logger.info('socket [' + socket.id + '] disconnected');
	});
});

if (setTimepoint) {
	timepoint.startJob(io);
}

webserver.listen(7777);